package com.origin.file.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.origin.common.util.MimeUtils;
import com.origin.common.util.security.SessionUtil;
import com.origin.file.component.FileDealComp;
import com.origin.file.entity.domain.*;
import com.origin.file.io.QiwenFile;
import com.origin.file.mapper.*;
import com.origin.file.service.IFiletransferService;
import com.origin.file.entity.dto.file.DownloadFileDTO;
import com.origin.file.entity.dto.file.PreviewDTO;
import com.origin.file.entity.dto.file.UploadFileDTO;
import com.origin.file.entity.vo.file.UploadFileVO;
import com.origin.oss.entity.FileInfo;
import com.origin.oss.enums.StorageType;
import com.origin.oss.enums.FileUploadStateEnum;
import com.origin.oss.exception.operation.DownloadException;
import com.origin.oss.exception.operation.UploadException;
import com.origin.oss.factory.FileUploaderFactory;
import com.origin.oss.operation.delete.Deleter;
import com.origin.oss.operation.delete.domain.DeleteFile;
import com.origin.oss.operation.download.Downloader;
import com.origin.oss.operation.download.domain.DownloadFile;
import com.origin.oss.operation.preview.Previewer;
import com.origin.oss.operation.preview.domain.PreviewFile;
import com.origin.oss.operation.upload.FileUploader;
import com.origin.oss.operation.upload.domain.FileSliceUpload;
import com.origin.oss.specification.OssClientAdapter;
import com.origin.oss.specification.domain.multipart.response.CompleteSliceUploadResp;
import com.origin.oss.service.FileInfoService;
import com.origin.oss.specification.domain.multipart.response.QuickUploadResp;
import com.origin.oss.util.FileStorageUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author origin
 */
@Service
@Slf4j
public class FiletransferService implements IFiletransferService {

    @Resource
    UserFileMapper userFileMapper;

    @Resource
    FileUploaderFactory ufopFactory;
    @Resource
    FileDealComp fileDealComp;
    @Resource
    UploadTaskDetailMapper uploadTaskDetailMapper;
    @Resource
    UploadTaskMapper uploadTaskMapper;
    @Resource
    ImageMapper imageMapper;

    @Resource
    PictureFileMapper pictureFileMapper;

    private final FileInfoService fileInfoService;
    private final OssClientAdapter ossClient;

    public static Executor exec = Executors.newFixedThreadPool(20);

    public FiletransferService(FileInfoService fileInfoService, OssClientAdapter ossClient) {
        this.fileInfoService = fileInfoService;
        this.ossClient = ossClient;
    }

    @Override
    public QuickUploadResp uploadFileSpeed(UploadFileDTO uploadFileDTO) {
        String filePath = uploadFileDTO.getFilePath();
        String relativePath = uploadFileDTO.getRelativePath();
        QiwenFile qiwenFile = new QiwenFile(filePath, relativePath.contains("/") ? relativePath : uploadFileDTO.getFilename(), false);

        QuickUploadResp quickUploadResp = ossClient.quickUpload(uploadFileDTO.getIdentifier());

        if (quickUploadResp.isSkipUpload()) {
            // 文件已经上传过了
            UserFile userFile = new UserFile(qiwenFile, SessionUtil.getUserId(), quickUploadResp.getFileId());
            try {
                userFileMapper.insert(userFile);
                fileDealComp.uploadESByUserFileId(userFile.getUserFileId());
            } catch (Exception e) {
                log.warn("极速上传文件冲突重命名处理: {}", JSON.toJSONString(userFile));
            }

            if (relativePath.contains("/")) {
                exec.execute(() -> fileDealComp.restoreParentFilePath(qiwenFile, SessionUtil.getUserId()));
            }
        }
//        else {
//            uploadFileVo.setSkipUpload(false);
//            List<Integer> uploadedList = uploadTaskDetailMapper.selectUploadedChunkNumList(uploadFileDTO.getIdentifier());
//            if (CollUtil.isNotEmpty(uploadedList)) {
//                uploadFileVo.setUploaded(uploadedList);
//            } else {
//                List<UploadTask> rslist = uploadTaskMapper
//                        .selectList(new LambdaQueryWrapper<UploadTask>().eq(UploadTask::getIdentifier, uploadFileDTO.getIdentifier()));
//                if (CollUtil.isEmpty(rslist)) {
//                    UploadTask uploadTask = new UploadTask();
//                    uploadTask.setIdentifier(uploadFileDTO.getIdentifier());
//                    uploadTask.setUploadTime(DateUtil.getCurrentTime());
//                    uploadTask.setUploadStatus(FileUploadStateEnum.UNCOMPLETE.getCode());
//                    uploadTask.setFileName(qiwenFile.getNameNotExtend());
//                    uploadTask.setFilePath(qiwenFile.getParent());
//                    uploadTask.setExtendName(qiwenFile.getExtendName());
//                    uploadTask.setUserId(SessionUtil.getUserId());
//                    uploadTaskMapper.insert(uploadTask);
//                }
//            }
//        }
        return quickUploadResp;
    }

    @Override
    public void uploadFileChunk(HttpServletRequest httpServletRequest, UploadFileDTO uploadFileDto, String userId) {
        FileUploader uploader = ufopFactory.getUploader();
        if (uploader == null) {
            log.error("上传失败，请检查 storageType 是否配置正确");
            throw new UploadException("上传失败");
        }

        // 上传切片, 文件由前端切片
        FileSliceUpload uploadFile = new FileSliceUpload();
        BeanUtil.copyProperties(uploadFileDto, uploadFile, true);

        StandardMultipartHttpServletRequest request = (StandardMultipartHttpServletRequest) httpServletRequest;

        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        if (!isMultipart) {
            throw new UploadException("未包含文件上传域");
        }

        Iterator<String> iter = request.getFileNames();
        while (iter.hasNext()) {
            List<MultipartFile> multipartFileList = request.getFiles(iter.next());
            if (CollUtil.isNotEmpty(multipartFileList)) {
                uploader.sliceUpload(multipartFileList.get(0), uploadFile);
                break;
            }
        }
    }

    @Override
    public void downloadFile(HttpServletResponse httpServletResponse, DownloadFileDTO downloadFileDTO) {
        UserFile userFile = userFileMapper.selectById(downloadFileDTO.getUserFileId());

        if (userFile.isFile()) {
            FileInfo fileInfo = fileInfoService.getByFileId(userFile.getFileId());
            Downloader downloader = ufopFactory.getDownloader(fileInfo.getStorageType());
            if (downloader == null) {
                log.error("下载失败，文件存储类型不支持下载，storageType:{}", fileInfo.getStorageType());
                throw new DownloadException("下载失败");
            }
            DownloadFile downloadFile = new DownloadFile();

            downloadFile.setFileUrl(fileInfo.getFileUrl());
            httpServletResponse.setContentLengthLong(fileInfo.getFileSize());
            downloader.download(httpServletResponse, downloadFile);
        } else {

            QiwenFile qiwenFile = new QiwenFile(userFile.getFilePath(), userFile.getFileName(), true);
            List<UserFile> userFileList = userFileMapper.selectUserFileByLikeRightFilePath(qiwenFile.getPath(), userFile.getUserId());
            List<String> userFileIds = userFileList.stream().map(UserFile::getUserFileId).collect(Collectors.toList());

            downloadUserFileList(httpServletResponse, userFile.getFilePath(), userFile.getFileName(), userFileIds);
        }
    }

    @Override
    public void downloadUserFileList(HttpServletResponse httpServletResponse, String filePath, String fileName, List<String> userFileIds) {
        String staticPath = FileStorageUtils.getStaticPath();
        String tempPath = staticPath + "temp" + File.separator;
        File tempDirFile = new File(tempPath);
        if (!tempDirFile.exists()) {
            tempDirFile.mkdirs();
        }

        FileOutputStream f = null;
        try {
            f = new FileOutputStream(tempPath + fileName + ".zip");
        } catch (FileNotFoundException e) {
            log.error("", e);
        }
        CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32());
        ZipOutputStream zos = new ZipOutputStream(csum);
        BufferedOutputStream out = new BufferedOutputStream(zos);

        try {
            for (String userFileId : userFileIds) {
                UserFile userFile1 = userFileMapper.selectById(userFileId);
                if (userFile1.isFile()) {
                    FileInfo fileBean = fileInfoService.getByFileId(userFile1.getFileId());
                    Downloader downloader = ufopFactory.getDownloader(fileBean.getStorageType());
                    if (downloader == null) {
                        log.error("下载失败，文件存储类型不支持下载，storageType:{}", fileBean.getStorageType());
                        throw new UploadException("下载失败");
                    }
                    DownloadFile downloadFile = new DownloadFile();
                    downloadFile.setFileUrl(fileBean.getFileUrl());
                    InputStream inputStream = downloader.getInputStream(downloadFile);
                    BufferedInputStream bis = new BufferedInputStream(inputStream);
                    try {
                        QiwenFile qiwenFile = new QiwenFile(StrUtil.removePrefix(userFile1.getFilePath(), filePath), userFile1.getFileName() + "." + userFile1.getExtendName(), false);
                        zos.putNextEntry(new ZipEntry(qiwenFile.getPath()));

                        byte[] buffer = new byte[1024];
                        int i = bis.read(buffer);
                        while (i != -1) {
                            out.write(buffer, 0, i);
                            i = bis.read(buffer);
                        }
                    } catch (IOException e) {
                        log.error("", e);
                    } finally {
                        IOUtils.closeQuietly(bis);
                        try {
                            out.flush();
                        } catch (IOException e) {
                            log.error("", e);
                        }
                    }
                } else {
                    QiwenFile qiwenFile = new QiwenFile(StrUtil.removePrefix(userFile1.getFilePath(), filePath), userFile1.getFileName(), true);
                    // 空文件夹的处理
                    zos.putNextEntry(new ZipEntry(qiwenFile.getPath() + QiwenFile.SEPARATOR));
                    // 没有文件，不需要文件的copy
                    zos.closeEntry();
                }
            }

        } catch (Exception e) {
            log.error("压缩过程中出现异常:" + e);
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                log.error("", e);
            }
        }
        String zipPath = "";
        try {
            Downloader downloader = ufopFactory.getDownloader(StorageType.LOCAL.getCode());
            DownloadFile downloadFile = new DownloadFile();
            downloadFile.setFileUrl("temp" + File.separator + fileName + ".zip");
            File tempFile = new File(FileStorageUtils.getStaticPath() + downloadFile.getFileUrl());
            httpServletResponse.setContentLengthLong(tempFile.length());
            downloader.download(httpServletResponse, downloadFile);
            zipPath = FileStorageUtils.getStaticPath() + "temp" + File.separator + fileName + ".zip";
        } catch (Exception e) {
            //org.apache.catalina.connector.ClientAbortException: java.io.IOException: Connection reset by peer
            if (e.getMessage().contains("ClientAbortException")) {
                //该异常忽略不做处理
            } else {
                log.error("下传zip文件出现异常：{}", e.getMessage());
            }

        } finally {
            File file = new File(zipPath);
            if (file.exists()) {
                file.delete();
            }
        }
    }

    @Override
    public void previewFile(HttpServletResponse httpServletResponse, PreviewDTO previewDTO) {
        UserFile userFile = userFileMapper.selectById(previewDTO.getUserFileId());
        FileInfo fileBean = fileInfoService.getByFileId(userFile.getFileId());
        Previewer previewer = ufopFactory.getPreviewer(fileBean.getStorageType());
        if (previewer == null) {
            log.error("预览失败，文件存储类型不支持预览，storageType:{}", fileBean.getStorageType());
            throw new UploadException("预览失败");
        }
        PreviewFile previewFile = new PreviewFile();
        previewFile.setFileUrl(fileBean.getFileUrl());
        try {
            if ("true".equals(previewDTO.getIsMin())) {
                previewer.imageThumbnailPreview(httpServletResponse, previewFile);
            } else {
                previewer.imageOriginalPreview(httpServletResponse, previewFile);
            }
        } catch (Exception e) {
            //org.apache.catalina.connector.ClientAbortException: java.io.IOException: 你的主机中的软件中止了一个已建立的连接。
            if (e.getMessage().contains("ClientAbortException")) {
                //该异常忽略不做处理
            } else {
                log.error("预览文件出现异常：{}", e.getMessage());
            }

        }

    }

    @Override
    public void previewPictureFile(HttpServletResponse httpServletResponse, PreviewDTO previewDTO) {
        byte[] bytesUrl = Base64.getDecoder().decode(previewDTO.getUrl());
        PictureFile pictureFile = new PictureFile();
        pictureFile.setFileUrl(new String(bytesUrl));
        pictureFile = pictureFileMapper.selectOne(new QueryWrapper<>(pictureFile));
        Previewer previewer = ufopFactory.getPreviewer(pictureFile.getStorageType());
        if (previewer == null) {
            log.error("预览失败，文件存储类型不支持预览，storageType:{}", pictureFile.getStorageType());
            throw new UploadException("预览失败");
        }
        PreviewFile previewFile = new PreviewFile();
        previewFile.setFileUrl(pictureFile.getFileUrl());
//        previewFile.setFileSize(pictureFile.getFileSize());
        try {

            String mime = MimeUtils.getMime(pictureFile.getExtendName());
            httpServletResponse.setHeader("Content-Type", mime);

            String fileName = pictureFile.getFileName() + "." + pictureFile.getExtendName();
            try {
                fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

            httpServletResponse.addHeader("Content-Disposition", "fileName=" + fileName);// 设置文件名

            previewer.imageOriginalPreview(httpServletResponse, previewFile);
        } catch (Exception e) {
            //org.apache.catalina.connector.ClientAbortException: java.io.IOException: 你的主机中的软件中止了一个已建立的连接。
            if (e.getMessage().contains("ClientAbortException")) {
                //该异常忽略不做处理
            } else {
                log.error("预览文件出现异常：{}", e.getMessage());
            }

        }
    }

    @Override
    public void deleteFile(FileInfo fileBean) {
        Deleter deleter = null;

        deleter = ufopFactory.getDeleter(fileBean.getStorageType());
        DeleteFile deleteFile = new DeleteFile();
        deleteFile.setFileUrl(fileBean.getFileUrl());
        deleter.delete(deleteFile);
    }

    @Override
    public Long selectStorageSizeByUserId(String userId) {
        return userFileMapper.selectStorageSizeByUserId(userId);
    }


//    private void resolveSuccessUploadFile(FileUploadResult uploadFileResult, UploadFileDTO uploadFileDTO, String userId) {
//        FileInfo fileBean = new FileInfo(uploadFileResult);
//        fileBean.setCreateUserId(userId);
//        try {
//            // 文件可能已经存在
//            fileMapper.insert(fileBean);
//        } catch (Exception e) {
//            log.warn("identifier Duplicate: {}", fileBean.getIdentifier());
//            fileBean = fileMapper.selectOne(new QueryWrapper<FileBean>().lambda().eq(FileBean::getIdentifier, fileBean.getIdentifier()));
//        }
//
//        String relativePath = uploadFileDTO.getRelativePath();
//        QiwenFile qiwenFile = new QiwenFile(uploadFileDTO.getFilePath()
//                , relativePath.contains("/") ? relativePath : uploadFileDTO.getFilename(), false);
//        UserFile userFile = new UserFile(qiwenFile, userId, fileBean.getFileId());
//        try {
//            userFileMapper.insert(userFile);
//            fileDealComp.uploadESByUserFileId(userFile.getUserFileId());
//        } catch (Exception e) {
//            UserFile userFile1 = userFileMapper.selectOne(new LambdaQueryWrapper<UserFile>()
//                    .eq(UserFile::getUserId, userFile.getUserId())
//                    .eq(UserFile::getFilePath, userFile.getFilePath())
//                    .eq(UserFile::getFileName, userFile.getFileName())
//                    .eq(UserFile::getExtendName, userFile.getExtendName())
//                    .eq(UserFile::getDeleteFlag, userFile.getDeleteFlag())
//                    .eq(UserFile::getIsDir, userFile.getIsDir()));
//            FileBean file1 = fileMapper.selectById(userFile1.getFileId());
//            if (!StringUtils.equals(fileBean.getIdentifier(), file1.getIdentifier())) {
//                log.warn("文件冲突重命名处理: {}", JSON.toJSONString(userFile1));
//                String fileName = fileDealComp.getRepeatFileName(userFile, userFile.getFilePath());
//                userFile.setFileName(fileName);
//                userFileMapper.insert(userFile);
//                fileDealComp.uploadESByUserFileId(userFile.getUserFileId());
//            }
//        }
//
//        if (relativePath.contains("/")) {
//            exec.execute(() -> fileDealComp.restoreParentFilePath(qiwenFile, userId));
//        }
//
//        uploadTaskDetailMapper.delete(new LambdaQueryWrapper<UploadTaskDetail>().eq(UploadTaskDetail::getIdentifier, uploadFileDTO.getIdentifier()));
//
//        uploadTaskMapper.update(null, new LambdaUpdateWrapper<UploadTask>()
//                .set(UploadTask::getUploadStatus, FileUploadStateEnum.SUCCESS.getCode())
//                .eq(UploadTask::getIdentifier, uploadFileDTO.getIdentifier()));
//
//        saveImage(uploadFileResult, fileBean.getFileId());
//
//        fileDealComp.parseMusicFile(uploadFileResult.getExtendName(), uploadFileResult.getStorageType().getCode(), uploadFileResult.getFileUrl(), fileBean.getFileId());
//    }

    private void resolveUnComplateUploadFile(UploadFileDTO uploadFileDTO) {
        QiwenFile qiwenFile = new QiwenFile(uploadFileDTO.getFilePath()
                , uploadFileDTO.getRelativePath().contains("/") ? uploadFileDTO.getRelativePath() : uploadFileDTO.getFilename(), false);
        UploadTaskDetail uploadTaskDetail = new UploadTaskDetail();
        uploadTaskDetail.setFilePath(qiwenFile.getParent());
        uploadTaskDetail.setFilename(qiwenFile.getNameNotExtend());
        uploadTaskDetail.setChunkNumber(uploadFileDTO.getChunkNumber());
        uploadTaskDetail.setChunkSize((int) uploadFileDTO.getChunkSize());
        uploadTaskDetail.setRelativePath(uploadFileDTO.getRelativePath());
        uploadTaskDetail.setTotalChunks(uploadFileDTO.getTotalChunks());
        uploadTaskDetail.setTotalSize((int) uploadFileDTO.getTotalSize());
        uploadTaskDetail.setIdentifier(uploadFileDTO.getIdentifier());
        uploadTaskDetailMapper.insert(uploadTaskDetail);
    }

    private void resolveFailUploadFile(UploadFileDTO uploadFileDTO) {
        uploadTaskDetailMapper.delete(new LambdaQueryWrapper<UploadTaskDetail>().eq(UploadTaskDetail::getIdentifier, uploadFileDTO.getIdentifier()));

        uploadTaskMapper.update(null, new LambdaUpdateWrapper<UploadTask>()
                .set(UploadTask::getUploadStatus, FileUploadStateEnum.FAIL.getCode())
                .eq(UploadTask::getIdentifier, uploadFileDTO.getIdentifier()));
    }

    private void saveImage(CompleteSliceUploadResp uploadFileResult, String fileId) {
        try {
            if (FileStorageUtils.isImageFile(uploadFileResult.getExtendName())) {
                BufferedImage src = uploadFileResult.getBufferedImage();
                Image image = new Image();
                image.setImageWidth(src.getWidth());
                image.setImageHeight(src.getHeight());
                image.setFileId(fileId);
                imageMapper.insert(image);
            }
        } catch (Exception e) {
            log.error("生成图片缩略图失败！", e);
        }
    }

    private String formatChatset(String str) {
        if (str == null) {
            return "";
        }
        if (java.nio.charset.Charset.forName("ISO-8859-1").newEncoder().canEncode(str)) {
            byte[] bytes = str.getBytes(StandardCharsets.ISO_8859_1);
            return new String(bytes, Charset.forName("GBK"));
        }
        return str;
    }

}
